using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace Jemgine.Hex
{
    public class VerticalCoordinateSystem : CoordinateSystem
    {
        public double Radius { get; private set; }
        public double Width { get; private set; }
        public double HalfWidth { get; private set; }
        public double Height { get; private set; }
        public double RowHeight { get; private set; }
        public double SuperOffsetX { get; internal set; }
        public double SuperOffsetY { get; internal set; }
        public SubdivisionTechniques CreationTechnique { get; internal set; }

        public double GetRadius() { return Radius; }
        public Orientation GetOrientation() { return Orientation.Vertical; }
        public Vector2 GetSuperOffset() { return new Vector2((float)SuperOffsetX, (float)SuperOffsetY); }

        public CoordinateSystem CreateSubSpace(SubdivisionTechniques Technique)
        {

            switch (Technique)
            {
                case SubdivisionTechniques.AlternateOrientation:
                    {
                        var Result = new HorizontalCoordinateSystem(Math.Sqrt(((Radius / 2) * (Radius / 2)) / 0.75));
                        Result.SuperOffsetX = HalfWidth - Result.Radius;
                        Result.SuperOffsetY = Result.HalfHeight;
                        Result.CreationTechnique = Technique;
                        return Result;
                    }
                case SubdivisionTechniques.DivideByThree:
                    {
                        var Result = new VerticalCoordinateSystem(Radius / 3.0);
                        Result.SuperOffsetX = HalfWidth - Result.HalfWidth;
                        Result.SuperOffsetY = Radius - Result.Radius;
                        Result.CreationTechnique = Technique;
                        return Result;
                    }
            }

            return null;
        }

        public VerticalCoordinateSystem(double radius)
        {
            this.Radius = radius;
            this.Height = 2 * radius;
            this.RowHeight = 1.5f * radius;
            this.HalfWidth = Math.Sqrt((radius * radius) - ((radius / 2) * (radius / 2)));
            this.Width = 2 * this.HalfWidth;
        }

        public Vector2 Origin(Coordinate tileCoordinate)
        {
            if (tileCoordinate.Y % 2 != 0)
                return new Vector2((float)((tileCoordinate.X * Width) + HalfWidth), (float)(tileCoordinate.Y * RowHeight));
            else
                return new Vector2((float)(tileCoordinate.X * Width), (float)(tileCoordinate.Y * RowHeight));
        }

        public Vector2 Center(Coordinate tileCoordinate)
        {
            return Origin(tileCoordinate) + new Vector2((float)HalfWidth, (float)Radius);
        }

        public Coordinate Neighbor(Coordinate tile, Direction direction)
        {
            if (tile.Y % 2 == 0)
            {
                switch (direction)
                {
                    case Direction.VerticalNorthEast: tile.Y += 1; break;
                    case Direction.VerticalEast: tile.X += 1; break;
                    case Direction.VerticalSouthEast: tile.Y -= 1; break;
                    case Direction.VerticalSouthWest: tile.Y -= 1; tile.X -= 1; break;
                    case Direction.VerticalWest: tile.X -= 1; break;
                    case Direction.VerticalNorthWest: tile.X -= 1; tile.Y += 1; break;
                    default: throw new InvalidOperationException("Invalid direction");
                }
            }
            else //This is an odd row.
            {
                switch (direction)
                {
                    case Direction.VerticalNorthEast: tile.X += 1; tile.Y += 1; break;
                    case Direction.VerticalEast: tile.X += 1; break;
                    case Direction.VerticalSouthEast: tile.X += 1; tile.Y -= 1; break;
                    case Direction.VerticalSouthWest: tile.Y -= 1; ; break;
                    case Direction.VerticalWest: tile.X -= 1; break;
                    case Direction.VerticalNorthWest: tile.Y += 1; break;
                    default: throw new InvalidOperationException("Invalid direction");
                }
            }

            return tile;
        }

        public Coordinate TileAt(Vector2 worldCoordinate)
        {
            double rise = Height - RowHeight;
            double slope = rise / HalfWidth;
            int X = (int)Math.Floor(worldCoordinate.X / Width);
            int Y = (int)Math.Floor(worldCoordinate.Y / RowHeight);

            double OffsetX = worldCoordinate.X - X * Width;
            double OffsetY = worldCoordinate.Y - Y * RowHeight;

            if (Y % 2 == 0) //Is this an even row?
            {
                //Section type A
                if (OffsetY < (-slope * OffsetX + rise)) //Point is below left line; inside SouthWest neighbor.
                {
                    X -= 1;
                    Y -= 1;
                }
                else if (OffsetY < (slope * OffsetX - rise)) //Point is below right line; inside SouthEast neighbor.
                    Y -= 1;
            }
            else
            {
                //Section type B
                if (OffsetX >= HalfWidth) //Is the point on the right side?
                {
                    if (OffsetY < (-slope * OffsetX + rise * 2.0f)) //Point is below bottom line; inside SouthWest neighbor.
                        Y -= 1;
                }
                else //Point is on the left side
                {
                    if (OffsetY < (slope * OffsetX)) //Point is below the bottom line; inside SouthWest neighbor.
                        Y -= 1;
                    else //Point is above the bottom line; inside West neighbor.
                        X -= 1;
                }
            }

            return new Coordinate(X, Y);
        }

        public Coordinate GetSubCenter(CoordinateSystem SubSpace, Coordinate Coord)
        {
            switch (SubSpace.CreationTechnique)
            {
                case SubdivisionTechniques.AlternateOrientation:
                    if (Coord.Y % 2 == 0)
                        return new Coordinate(Coord.X * 2, Coord.Y / 2 * 3);
                    else
                        return new Coordinate((Coord.X * 2) + 1, (Coord.Y / 2 * 3) + 1);
                case SubdivisionTechniques.DivideByThree:
                    if (Coord.Y % 2 == 0)
                        return new Coordinate(Coord.X * 3, Coord.Y * 3);
                    else
                        return new Coordinate((Coord.X * 3) + 1, Coord.Y * 3);
                default:
                    throw new InvalidProgramException();
            }
        }

    }
}
